{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dcb0b55d-1f15-4461-81e2-9660d3096946",
   "metadata": {},
   "source": [
    "论文： https://arxiv.org/pdf/1312.4400"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62edd5dd-de1a-4af6-adbb-c39a2c9bd1ee",
   "metadata": {},
   "source": [
    "# 网络中的网络（NiN）\n",
    "`sec_nin`\n",
    "\n",
    "LeNet、AlexNet和VGG都有一个共同的设计模式：通过一系列的卷积层与汇聚层来提取空间结构特征；然后通过全连接层对特征的表征进行处理。  \n",
    "AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。  \n",
    "或者，可以想象在这个过程的早期使用全连接层。然而，如果使用了全连接层，可能会完全放弃表征的空间结构。  \n",
    "*网络中的网络*（*NiN*）提供了一个非常简单的解决方案：在每个像素的通道上分别使用多层感知机 `Lin.Chen.Yan.2013`  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "324028bf",
   "metadata": {
    "origin_pos": 0
   },
   "source": [
    "\n",
    "## NiN块\n",
    "\n",
    "回想一下，卷积层的输入和输出由四维张量组成，张量的每个轴分别对应样本、通道、高度和宽度。  \n",
    "另外，全连接层的输入和输出通常是分别对应于样本和特征的二维张量。   \n",
    "NiN的想法是在每个像素位置（针对每个高度和宽度）应用一个全连接层。    \n",
    "如果我们将权重连接到每个空间位置，我们可以将其视为$1\\times 1$卷积层（如 `sec_channels`中所述），或作为在每个像素位置上独立作用的全连接层。    \n",
    "从另一个角度看，即将空间维度中的每个像素视为单个样本，将通道维度视为不同特征（feature）。  \n",
    "\n",
    "`fig_nin`说明了VGG和NiN及它们的块之间主要架构差异。  \n",
    "NiN块以一个普通卷积层开始，后面是两个$1 \\times 1$的卷积层。这两个$1 \\times 1$卷积层充当带有ReLU激活函数的逐像素全连接层。  \n",
    "第一层的卷积窗口形状通常由用户设置。  \n",
    "随后的卷积窗口形状固定为$1 \\times 1$。  \n",
    "\n",
    "![对比 VGG 和 NiN 及它们的块之间主要架构差异。](../img/nin.svg)\n",
    "`fig_nin`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81db1a50-2ecd-4680-bb15-fda67a446bdb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "parent_directory = os.path.dirname(os.getcwd())\n",
    "if parent_directory not in sys.path:\n",
    "    sys.path.append(parent_directory)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b116832",
   "metadata": {
    "origin_pos": 2,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from d2l import torch as d2l\n",
    "\n",
    "\n",
    "def nin_block(in_channels, out_channels, kernel_size, strides, padding):\n",
    "    return nn.Sequential(\n",
    "        nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),\n",
    "        nn.ReLU(),\n",
    "        nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU(),\n",
    "        nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc11a427",
   "metadata": {
    "origin_pos": 5
   },
   "source": [
    "## NiN模型\n",
    "\n",
    "最初的NiN网络是在AlexNet后不久提出的，显然从中得到了一些启示。  \n",
    "NiN使用窗口形状为$11\\times 11$、$5\\times 5$和$3\\times 3$的卷积层，输出通道数量与AlexNet中的相同。  \n",
    "每个NiN块后有一个最大汇聚层，汇聚窗口形状为$3\\times 3$，步幅为2。  \n",
    "\n",
    "NiN和AlexNet之间的一个显著区别是NiN完全取消了全连接层。  \n",
    "相反，NiN使用一个NiN块，其输出通道数等于标签类别的数量。最后放一个*全局平均汇聚层*（global average pooling layer），生成一个对数几率\t（logits）。NiN设计的一个优点是，它显著减少了模型所需参数的数量。然而，在实践中，这种设计有时会增加训练模型的时间。  \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ba4ca30",
   "metadata": {
    "origin_pos": 7,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "net = nn.Sequential(\n",
    "    nin_block(1, 96, kernel_size=11, strides=4, padding=0),\n",
    "    nn.MaxPool2d(3, stride=2),\n",
    "    nin_block(96, 256, kernel_size=5, strides=1, padding=2),\n",
    "    nn.MaxPool2d(3, stride=2),\n",
    "    nin_block(256, 384, kernel_size=3, strides=1, padding=1),\n",
    "    nn.MaxPool2d(3, stride=2),\n",
    "    nn.Dropout(0.5),\n",
    "    # 标签类别数是10\n",
    "    nin_block(384, 10, kernel_size=3, strides=1, padding=1),\n",
    "    nn.AdaptiveAvgPool2d((1, 1)),\n",
    "    # 将四维的输出转成二维的输出，其形状为(批量大小,10)\n",
    "    nn.Flatten())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76c9e9ac",
   "metadata": {
    "origin_pos": 10
   },
   "source": [
    "我们创建一个数据样本来[**查看每个块的输出形状**]。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2917eb46",
   "metadata": {
    "origin_pos": 12,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "X = torch.rand(size=(1, 1, 224, 224))\n",
    "for layer in net:\n",
    "    X = layer(X)\n",
    "    print(layer.__class__.__name__,'output shape:\\t', X.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95a4c245",
   "metadata": {
    "origin_pos": 15
   },
   "source": [
    "## 训练模型\n",
    "\n",
    "和以前一样，我们使用Fashion-MNIST来训练模型。训练NiN与训练AlexNet、VGG时相似。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c362e21",
   "metadata": {
    "origin_pos": 16,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "lr, num_epochs, batch_size = 0.1, 10, 128\n",
    "train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)\n",
    "d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "847261ce",
   "metadata": {
    "origin_pos": 17
   },
   "source": [
    "## 小结\n",
    "\n",
    "* NiN使用由一个卷积层和多个$1\\times 1$卷积层组成的块。该块可以在卷积神经网络中使用，以允许更多的每像素非线性。\n",
    "* NiN去除了容易造成过拟合的全连接层，将它们替换为全局平均汇聚层（即在所有位置上进行求和）。该汇聚层通道数量为所需的输出数量（例如，Fashion-MNIST的输出为10）。\n",
    "* 移除全连接层可减少过拟合，同时显著减少NiN的参数。\n",
    "* NiN的设计影响了许多后续卷积神经网络的设计。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "d2l-zh-312",
   "language": "python",
   "name": "d2l-zh-312"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  },
  "required_libs": []
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
